if (!require("pacman")) install.packages("pacman")Lade nötiges Paket: pacman
pacman::p_load(psych, tidyverse)
data(sat.act)
df_beispiel <-as_tibble(sat.act)Data Wrangling bezeichnet im Kontext der psychologischen Datenanalyse mit R den systematischen Prozess der Aufbereitung, Bereinigung und Umstrukturierung von Rohdaten, damit sie für statistische Auswertungen und Interpretationen nutzbar werden. Ziel ist es, aus unübersichtlichen Rohdaten ein strukturiertes und hochwertiges Daten-Set zu erzeugen, das sich zuverlässig und nachvollziehbar für weiterführende statistische Analysen, Modellierungen und Visualisierungen in R nutzen lässt.
Im Folgenden werden dazu verschiedene Funktionen und Techniken dargestellt.
In diesem Kurs arbeiten wir mit R- bzw. Quarto-Notebooks. Diese enthalten sowohl normalen Text als auch “Chunks” von R-Code. Chunks sind Abschnitte von R-Code. Diese Chunks lassen sich mit einem Klick auf Code –> Insert Chunk (oder der Tastenkombination Alt + Ctrl + I / unter Mac: Alt + Cmd + I) einfügen. Diese Chunks lassen sich mit einem Klick auf den grünen Play-Button ganz rechts ausführen. Der Pfeil nach unten Button (zweiter von Rechts) führt alle Chunks, die vor dem gewählten Chunk gelagert sind, aus.
Zunächst laden wir die Pakete, die wir für die Sitzung brauchen.
if (!require("pacman")) install.packages("pacman")Lade nötiges Paket: pacman
pacman::p_load(psych, tidyverse)
data(sat.act)
df_beispiel <-as_tibble(sat.act)Für jede Funktion in einem geladenen Paket können Sie einfach ? vor den Funktionsnamen schreiben, um das Hilfemenü bzw. ihre Dokumentation aufzurufen. Dies hilft Ihnen, den Zweck der Funktion, ihre Argumente und Ausgaben zu verstehen. Sie können auch auf den Namen der Funktion klicken (an beliebiger Stelle im Namen) und “F1” drücken.
?head
head(df_beispiel)Fast alles in R geschieht mittels Funktionen. Funktionen nehmen Inputs und transformieren sie nach den Regeln der Funktion in Outputs. Die Funktion kann mittels Argumenten gesteuert werden.
head, mean) und mit Klammern () gekennzeichet. In der Klammer befinden sich die Argumente der Funktion, mit einem Komma getrennt. Argumente können benannt oder unbenannt sein. Werden Sie nicht benannt, folgt R der Reihenfolge der erwarteten Argumente, wie sie in der Funktionsdokumentation beschrieben sind.10 bei head()na.rm = TRUE bei mean(), wobei na.rm der Name und TRUE der Inhalt des Argument sind.head(df_beispiel, 10)# A tibble: 10 × 6
gender education age ACT SATV SATQ
<int> <int> <int> <int> <int> <int>
1 2 3 19 24 500 500
2 2 3 23 35 600 500
3 2 3 20 21 480 470
4 1 4 27 26 550 520
5 1 2 33 31 600 550
6 1 5 26 28 640 640
7 2 5 30 36 610 500
8 1 3 19 22 520 560
9 2 4 23 22 400 600
10 2 5 40 35 730 800
mean(df_beispiel$age, na.rm = TRUE)[1] 25.59429
<- führt die Zuweisung (assignment) von Inhalten der rechten Seite unter dem Namen auf der linken Seite aus. Wir nennen die zugewiesenen Inhalte, etwas verkürzt gesagt, Objekte oder Variablen.durchschnitt_von_SATV <- mean(df_beispiel$SATV, na.rm = TRUE)In diesem Beispiel ist durchschnitt_von_SATV der Name des Objekts/Variable.
Die häufigsten Typen von Objekten/Variablen sind:
Einen Vektor erstellt man mit der c() Funktionen
vektor_numerisch <- c(1,2,3,4,5)
vektor_character <- c("Zytglogge", "Hirschengraben", "Bahnhof", "Bundesplatz", "Viktoriaplatz")
datenframe <- data.frame(Nummer = vektor_numerisch, Ort = vektor_character)
datenframe Nummer Ort
1 1 Zytglogge
2 2 Hirschengraben
3 3 Bahnhof
4 4 Bundesplatz
5 5 Viktoriaplatz
tibble_beispiel <- tibble(Nummer = vektor_numerisch, Ort = vektor_character)
tibble_beispiel# A tibble: 5 × 2
Nummer Ort
<dbl> <chr>
1 1 Zytglogge
2 2 Hirschengraben
3 3 Bahnhof
4 4 Bundesplatz
5 5 Viktoriaplatz
Wie wir sehen, sind Datenframes i.d.R. aus Vektoren der gleichen Länge, aber oft unterschiedlichen Typs, aufgebaut.
beispielliste <- list(x1 = vektor_numerisch, x2 = vektor_character, c(6,7,8), x3 = tibble_beispiel, "test")
beispielliste$x1
[1] 1 2 3 4 5
$x2
[1] "Zytglogge" "Hirschengraben" "Bahnhof" "Bundesplatz"
[5] "Viktoriaplatz"
[[3]]
[1] 6 7 8
$x3
# A tibble: 5 × 2
Nummer Ort
<dbl> <chr>
1 1 Zytglogge
2 2 Hirschengraben
3 3 Bahnhof
4 4 Bundesplatz
5 5 Viktoriaplatz
[[5]]
[1] "test"
Listen sind extrem flexibel, was ihre Anhalte angeht - wir können quasi alles reinpacken. Das macht sie teils aber auch schwer zu durchschauen. Wenn möglich empfiehlt es sich, mit tibble/data.frame zu arbeiten; dies spart kognitive Ressourcen.
%>% ist der ursprüngliche Pipe-Operator, der für das Paket {magrittr} entwickelt wurde und in den gesamten tidyverse-Paketen verwendet wird. Er ist etwas langsamer, aber auch flexibler. |> ist eine neuere Version des Pipe-Operators, die in Base-R integriert wurde. Sie ist etwas schneller, aber weniger flexibel.
Die Ausgabe des linken Teils der Pipe wird als Eingabe für den rechten Teil der Pipe verwendet, in der Regel als erstes Argument oder als Datenargument. Effektiv transportiert die Pipe den Inhalt von links nach rechts weiter. Schreiben lässt sich die Pipe mit den Tasten Ctrl + Shift + M (unter Windows; unter Mac Cmd + Shift + M?).
# use a function without the pipe
example_without_pipe <- select(df_beispiel, gender, ACT)
# use a function with the pipe.
example_with_pipe <- df_beispiel |>
select(gender, ACT)
example_without_pipe# A tibble: 700 × 2
gender ACT
<int> <int>
1 2 24
2 2 35
3 2 21
4 1 26
5 1 31
6 1 28
7 2 36
8 1 22
9 2 22
10 2 35
# ℹ 690 more rows
example_with_pipe# A tibble: 700 × 2
gender ACT
<int> <int>
1 2 24
2 2 35
3 2 21
4 1 26
5 1 31
6 1 28
7 2 36
8 1 22
9 2 22
10 2 35
# ℹ 690 more rows
# check they produce identical results
identical(example_without_pipe, example_with_pipe)[1] TRUE
Der Pipe-Operator ermöglicht es uns, Code zu schreiben, der von oben nach unten gelesen wird und einer Abfolge von Schritten folgt – so, wie Menschen Schritte organisieren und beschreiben. Ohne den Pipe-Operator wird der Code von innen nach aussen geschrieben, auf eine Weise, die der Computer versteht, aber für Menschen weniger intuitiv ist. Die Unterscheide in der Lesbarkeit zeigen sich vor allem bei komplexeren, verketteten Funktionen.
Wir verwenden im Folgenden einen Datenframe, wählen mit select() Spalten aus, und erstellen ihre Mittelwerte mit colMeans(). Ohne Pipe lesen wir von innen nach aussen: select(…), dann colMeans(…); mit Pipe lesen und schreiben wir: Objekt - select() - colMeans(), was einfacher nachzuvollziehen ist. Jede Funktion macht etwas, die Pipe gibt jeweils den transformierten Inhalt weiter an die nächste Funktion.
# use a function without the pipe
example_without_pipe <- colMeans(select(df_beispiel, SATV, SATQ), na.rm = TRUE)
example_without_pipe SATV SATQ
612.2343 610.2169
# use a function with the pipe.
example_with_pipe <- df_beispiel |>
select(SATV, SATQ) |>
colMeans(na.rm = TRUE)
# check they produce identical results
identical(example_without_pipe, example_with_pipe)[1] TRUE
Die meiste Zeit verbringen wir mit Data Frames - effektiv Tabellen. Wie können wir herausfinden, welche Variablen sich in einem Data Frame befinden? Wir können den Data Frame anzeigen, aber es kann auch hilfreich sein, ihn auszugeben. Zu wissen, welche Variablen vorhanden sind, ist einer der ersten Schritte, um mit den Daten zu arbeiten.
# print all column names
colnames(df_beispiel)[1] "gender" "education" "age" "ACT" "SATV" "SATQ"
# print all column names as a vector using the pipe
df_beispiel |>
colnames()[1] "gender" "education" "age" "ACT" "SATV" "SATQ"
Oft sind die Variablennamen nicht intuitiv. Ein früher Schritt bei jeder Datenaufbereitung ist es, sie mithilfe der Funktion rename() intuitiver zu gestalten. Als Argumente schreiben wir jeweils neuer_name = alter_name.
df_beispiel_renamed <- df_beispiel |>
rename(Alter = age,
Bildung = education) Es gibt verschieden Möglichkeiten, Inhalte aus Variablen auszuwählen und zu extrahieren:
Im ersten Beispiel wollen wir die Variable x aus unserem Dataframe df_beispiel auswählen.
df_beispiel |> select(ACT) # select mit pipe# A tibble: 700 × 1
ACT
<int>
1 24
2 35
3 21
4 26
5 31
6 28
7 36
8 22
9 22
10 35
# ℹ 690 more rows
df_beispiel[,"ACT"] # []# A tibble: 700 × 1
ACT
<int>
1 24
2 35
3 21
4 26
5 31
6 28
7 36
8 22
9 22
10 35
# ℹ 690 more rows
df_beispiel$ACT # $ [1] 24 35 21 26 31 28 36 22 22 35 32 29 21 35 27 27 33 32 28 32 28 30 31 30 31
[26] 30 30 21 28 33 28 24 27 31 33 29 28 24 27 31 28 29 30 36 36 26 33 27 23 33
[51] 35 28 28 33 34 26 36 28 18 34 34 32 36 34 21 31 22 29 29 24 26 29 28 32 32
[76] 27 28 32 15 21 26 25 31 26 28 33 24 32 31 30 32 36 32 30 27 35 29 33 30 32
[101] 26 36 36 27 26 29 23 23 29 32 32 30 28 23 36 26 36 34 23 28 23 28 32 16 29
[126] 29 29 32 26 23 35 28 25 23 36 30 30 33 31 28 35 32 26 34 34 32 24 32 23 35
[151] 24 30 26 26 30 34 19 23 31 28 32 25 26 31 34 31 30 36 32 33 29 25 30 34 32
[176] 29 25 32 33 35 26 24 23 35 27 28 27 28 32 34 27 31 17 25 36 29 30 29 28 28
[201] 30 30 31 29 28 26 31 25 21 24 36 34 32 23 36 34 28 19 31 21 19 22 30 18 36
[226] 28 24 28 23 30 22 23 25 25 24 30 30 24 22 22 27 27 35 16 28 25 23 32 25 28
[251] 35 28 24 27 30 34 35 33 19 25 35 26 24 20 30 32 30 29 28 23 36 25 29 20 32
[276] 24 30 33 21 20 32 30 31 25 18 25 26 29 33 31 25 27 36 32 32 26 23 34 27 30
[301] 30 28 25 20 26 33 23 33 25 27 26 28 28 35 21 28 29 32 30 18 32 32 27 17 28
[326] 21 30 25 32 35 32 16 27 27 20 31 30 24 26 19 24 27 35 27 32 26 25 29 36 30
[351] 25 22 27 23 34 31 33 31 26 35 33 29 28 27 27 31 31 32 22 35 25 25 23 26 27
[376] 27 34 30 32 26 25 29 22 28 35 24 27 32 17 18 20 35 32 24 32 29 28 27 27 20
[401] 22 33 30 34 28 32 30 23 34 26 32 22 29 23 35 32 31 27 29 29 33 35 21 24 29
[426] 30 30 32 28 28 25 28 33 32 20 21 28 35 26 3 21 21 32 31 27 35 32 24 34 32
[451] 31 36 23 28 35 22 26 29 29 21 31 36 25 27 17 32 32 22 26 30 25 25 34 24 20
[476] 32 25 21 22 35 20 23 23 29 21 28 35 34 36 15 34 28 24 23 22 32 21 35 26 15
[501] 32 28 28 25 30 28 20 22 20 18 32 35 30 25 26 21 24 16 30 28 34 35 25 34 27
[526] 31 27 34 31 27 27 36 30 15 23 26 35 33 34 35 27 31 33 29 21 19 25 30 21 23
[551] 32 21 25 27 29 20 32 28 32 32 35 18 32 31 20 32 33 34 31 30 34 32 28 34 31
[576] 29 34 32 31 32 32 27 33 30 27 31 33 31 33 30 32 31 33 35 32 32 33 36 26 34
[601] 36 31 30 24 34 34 29 30 31 31 26 32 30 28 32 32 25 32 36 25 29 28 32 28 33
[626] 35 27 32 35 29 26 31 34 26 35 35 28 27 20 31 34 22 35 22 22 35 34 32 31 25
[651] 32 31 29 32 27 27 34 22 36 36 34 33 34 21 28 29 25 31 29 31 22 23 33 36 30
[676] 30 36 32 33 28 31 32 34 33 36 30 30 27 33 32 33 25 26 27 26 30 27 31 32 25
Wie wir sehen, behalten |> und [] die Struktur des Inputs (ein Datenframe) standardmässig bei (ihr Output ist identisch), während $ die Struktur “fallen lässt” und eine simplere Struktur wählt (Vektor statt Datenframe). Entsprechend ist der Output auch anders.
Im zweiten Beispiel wollen wir die Variablen x und y und die ersten zwei Zeilen aus unserem Dataframe df_beispiel auswählen. Nach dem “tidy” Verfahren wählen wir dafür Datenframe |> select() |> slice(). Nach dem “klassischen” Verfahren wählen wir in [] zuerst die Zeilen, dann die Spalten aus. Die Spalten müssen wir hier als richtigen Vektor eingeben, d.h. mittels c() “x”und “y” (in Anführungszeichen) angeben.
df_beispiel |> select(SATV, SATQ) |> slice(1:2)# A tibble: 2 × 2
SATV SATQ
<int> <int>
1 500 500
2 600 500
df_beispiel[1:2,c("SATV", "SATQ")]# A tibble: 2 × 2
SATV SATQ
<int> <int>
1 500 500
2 600 500
c(df_beispiel$SATV[1:2], df_beispiel$SATQ[1:2])[1] 500 600 500 500
Listen sind etwas komplizierter. Hier wird analog zu select() pluck() verwendet.
beispielliste <- list(x1 = vektor_numerisch, x2 = vektor_character, c(6,7,8), x3 = tibble_beispiel, "test")
# tidy
beispielliste |> pluck(2)[1] "Zytglogge" "Hirschengraben" "Bahnhof" "Bundesplatz"
[5] "Viktoriaplatz"
beispielliste |> pluck("x2")[1] "Zytglogge" "Hirschengraben" "Bahnhof" "Bundesplatz"
[5] "Viktoriaplatz"
# [] und [[]]
beispielliste[2]$x2
[1] "Zytglogge" "Hirschengraben" "Bahnhof" "Bundesplatz"
[5] "Viktoriaplatz"
beispielliste[[2]][1] "Zytglogge" "Hirschengraben" "Bahnhof" "Bundesplatz"
[5] "Viktoriaplatz"
# $ - geht nur mit benannten Listenelementen
beispielliste$x2[1] "Zytglogge" "Hirschengraben" "Bahnhof" "Bundesplatz"
[5] "Viktoriaplatz"
Beim Data Wrangling bearbeiten wir häufig Gruppen von Variablen / Spalten. Damit wir nicht immer jede einzelne Variable ausschreiben müssen, gibt es verschiedene Methoden. Hilfreich sind vor allem die sogenannten “tidyselect” Helferfunktionen. Diese können wir in select(), aber auch vielen anderen Funktionen verwenden, bei der wir Spalten aus einem Datensatz ansteuern.
Die häufigsten Helfer sind :, c(), starts_with(), ends_with(), contains(), all_of(), num_range().
names(df_beispiel)[1] "gender" "education" "age" "ACT" "SATV" "SATQ"
df_beispiel |> select(age:gender)# A tibble: 700 × 3
age education gender
<int> <int> <int>
1 19 3 2
2 23 3 2
3 20 3 2
4 27 4 1
5 33 2 1
6 26 5 1
7 30 5 2
8 19 3 1
9 23 4 2
10 40 5 2
# ℹ 690 more rows
df_beispiel |> select(c(age,gender))# A tibble: 700 × 2
age gender
<int> <int>
1 19 2
2 23 2
3 20 2
4 27 1
5 33 1
6 26 1
7 30 2
8 19 1
9 23 2
10 40 2
# ℹ 690 more rows
df_beispiel |> select(starts_with("S"))# A tibble: 700 × 2
SATV SATQ
<int> <int>
1 500 500
2 600 500
3 480 470
4 550 520
5 600 550
6 640 640
7 610 500
8 520 560
9 400 600
10 730 800
# ℹ 690 more rows
df_beispiel |> select(ends_with("V"))# A tibble: 700 × 1
SATV
<int>
1 500
2 600
3 480
4 550
5 600
6 640
7 610
8 520
9 400
10 730
# ℹ 690 more rows
df_beispiel |> select(contains("SAT"))# A tibble: 700 × 2
SATV SATQ
<int> <int>
1 500 500
2 600 500
3 480 470
4 550 520
5 600 550
6 640 640
7 610 500
8 520 560
9 400 600
10 730 800
# ℹ 690 more rows
meine_variablen <- c("SATV", "SATQ", "gender")
df_beispiel |> select(all_of(meine_variablen))# A tibble: 700 × 3
SATV SATQ gender
<int> <int> <int>
1 500 500 2
2 600 500 2
3 480 470 2
4 550 520 1
5 600 550 1
6 640 640 1
7 610 500 2
8 520 560 1
9 400 600 2
10 730 800 2
# ℹ 690 more rows
Wir können auch mit logischen Operatoren arbeiten und diese verketten. Zu logischen Operatoren siehe auch: Einführung in R, Kapitel 2.1.2.
data(bfi)
names(bfi) #Big Five Items, OCEAN mit je 5 Items sowie gender, education und age [1] "A1" "A2" "A3" "A4" "A5" "C1"
[7] "C2" "C3" "C4" "C5" "E1" "E2"
[13] "E3" "E4" "E5" "N1" "N2" "N3"
[19] "N4" "N5" "O1" "O2" "O3" "O4"
[25] "O5" "gender" "education" "age"
# bfi von normalen data.frame zu tibble upgraden
bfi <- as_tibble(bfi)
bfi |> select(starts_with("E")) |> head() # auch education dabei# A tibble: 6 × 6
E1 E2 E3 E4 E5 education
<int> <int> <int> <int> <int> <int>
1 3 3 3 4 4 NA
2 1 1 6 4 3 NA
3 2 4 4 4 5 NA
4 5 3 4 4 4 NA
5 2 2 5 4 5 NA
6 2 1 6 5 6 3
bfi |> select(starts_with("E") & !all_of("education")) |> head() # nur die Extraversion Items, kein education# A tibble: 6 × 5
E1 E2 E3 E4 E5
<int> <int> <int> <int> <int>
1 3 3 3 4 4
2 1 1 6 4 3
3 2 4 4 4 5
4 5 3 4 4 4
5 2 2 5 4 5
6 2 1 6 5 6
# oder eleganter mit num_range()
bfi |> select(num_range("E", 1:5)) |> head() # nur die Extraversion Items# A tibble: 6 × 5
E1 E2 E3 E4 E5
<int> <int> <int> <int> <int>
1 3 3 3 4 4
2 1 1 6 4 3
3 2 4 4 4 5
4 5 3 4 4 4
5 2 2 5 4 5
6 2 1 6 5 6
bfi |> select(starts_with("E"), starts_with("C")) |> head() # "," wird als einschliessendes oder (OR) gelesen# A tibble: 6 × 11
E1 E2 E3 E4 E5 education C1 C2 C3 C4 C5
<int> <int> <int> <int> <int> <int> <int> <int> <int> <int> <int>
1 3 3 3 4 4 NA 2 3 3 4 4
2 1 1 6 4 3 NA 5 4 4 3 4
3 2 4 4 4 5 NA 4 5 4 2 5
4 5 3 4 4 4 NA 4 4 3 5 5
5 2 2 5 4 5 NA 4 4 5 3 2
6 2 1 6 5 6 3 6 6 6 1 3
mutate() wird genutzt, um neue Spalten anzulegen oder die Inhalte bestehender Spalten zu ändern. is used to create new columns or to change the contents of existing ones. Oft verändern wir Variablen bei einer Analyse. Typisch ist etwa die Zentrierung von Variablen auf den Mittelwert der Stichprobe (grand mean; damit bekommen die Variablen einen Mittelwert von 0) oder die z-Standardisierung (Variablen bekommen einen Mittelwert von 0 und eine Standardabweichung von 1).
df_beispiel <- df_beispiel |> mutate(ACT_c = scale(ACT, center = TRUE, scale = FALSE),
ACT_z = scale(ACT, center = TRUE, scale = TRUE))
df_beispiel# A tibble: 700 × 8
gender education age ACT SATV SATQ ACT_c[,1] ACT_z[,1]
<int> <int> <int> <int> <int> <int> <dbl> <dbl>
1 2 3 19 24 500 500 -4.55 -0.943
2 2 3 23 35 600 500 6.45 1.34
3 2 3 20 21 480 470 -7.55 -1.56
4 1 4 27 26 550 520 -2.55 -0.528
5 1 2 33 31 600 550 2.45 0.509
6 1 5 26 28 640 640 -0.547 -0.113
7 2 5 30 36 610 500 7.45 1.55
8 1 3 19 22 520 560 -6.55 -1.36
9 2 4 23 22 400 600 -6.55 -1.36
10 2 5 40 35 730 800 6.45 1.34
# ℹ 690 more rows
Neben der Mutation/Transformation von Spalten ist das Filtern von Zeilen ein typisches Anliegen bei der Datenaufbereitung. Dies geht mittels filter(). Du kannst den logischen Test für das Filtern auf verschiedene Arten angeben, einschließlich Gleichheit (==), Negation (!=) oder Zugehörigkeit (%in%). Es ist oft besser zu definieren, was du möchtest (mithilfe von Gleichheit oder Zugehörigkeit), anstatt zu definieren, was du nicht möchtest (Negation), da Negationen weniger robust gegenüber neuen Daten mit ungewöhnlichen Werten sind, die du beim Schreiben des Codes nicht bedacht hast. Zum Beispiel könntest du gender != 2 angeben, aber das würde non binary nicht erfassen.
df_beispiel_w <- df_beispiel |> filter(gender == 2)Du kannst auch mehrere Kriterien in deinem Filteraufruf verwenden, bei denen beide erfüllt sein müssen (x & y) oder bei denen eines von beiden erfüllt sein muss (x | y).
df_beispiel_w_and_edu <- df_beispiel |> filter(gender == 2 & education > 2)
df_beispiel_w_or_edu <- df_beispiel |> filter(gender == 2 | education > 2)
# note that these provide different results - make sure you understand why
identical(df_beispiel_w_and_edu, df_beispiel_w_or_edu)[1] FALSE
as.factor()).Führe zunächst jeden Schritt separat und sequentiell aus. Verknüpfe dann alle Funktionen/Schritte mittels mehrerer Pipes. Das führt dazu, dass weniger Objekte in deiner Umgebung existieren und somit weniger Verwirrung oder Fehlerpotenzial entsteht. Genereller Tipp: Wir lösen Programmierprobleme leichter, indem wir sie in kleinere Aufgaben und Probleme zerlegen, diese jeweils einzeln zum Laufen bringen und sie dann wieder zusammenfügen. Wenn man nur das Endprodukt sieht, könnte man leicht denken, der/die Autor:in hätte den Code genau so geschrieben, wie er aussieht. Dabei hat er/sie oft viel ausführlichere Codeabschnitte geschrieben und diese anschließend zusammengeführt.
# auswählen
df_auswahl <- df_beispiel
# neue variable erstellen
df_neuevariable <- df_auswahl
# filtern
df_filter <- df_neuevariable
# jetzt alles zusammen
df_kombiniert <- df_beispiel # auswählen
df_auswahl <- df_beispiel |> select(education, SATQ)
# neue variable erstellen
df_neuevariable <- df_auswahl |> mutate(
education = as.factor(education),
SATQ_c = scale(SATQ, center = TRUE, scale = FALSE))
# filtern
df_filter <- df_neuevariable |> filter(education == 1)
# jetzt alles zusammen
df_kombiniert <- df_beispiel |>
select(education, SATQ) |>
mutate(
education = as.factor(education),
SATQ_c = scale(SATQ, center = TRUE, scale = FALSE)) |>
filter(education == 1)
# identical(df_filter, df_kombiniert) # checkEs ist sehr häufig, dass wir Zusammenfassungen über Zeilen hinweg erstellen müssen. Zum Beispiel, um den Mittelwert und die Standardabweichung einer Spalte wie age zu berechnen. Das kann mit summarize() erledigt werden. Denk daran: mutate() erstellt neue Spalten oder ändert den Inhalt bestehender Spalten, ohne die Anzahl der Zeilen zu verändern. summarize() hingegen reduziert einen Datensatz auf eine einzelne Zeile.
# mean
df_beispiel |> summarize(mean_age = mean(age, na.rm = TRUE))# A tibble: 1 × 1
mean_age
<dbl>
1 25.6
# SD
df_beispiel |>
summarize(sd_age = sd(age, na.rm = TRUE))# A tibble: 1 × 1
sd_age
<dbl>
1 9.50
# mean and SD with rounding, illustrating how multiple summarizes can be done in one function call
df_beispiel |>
summarize(mean_age = mean(age, na.rm = TRUE),
mean_age = round(mean_age, digits = 2),
sd_age = sd(age, na.rm = TRUE),
sd_age = round(sd_age, digits = 2))# A tibble: 1 × 2
mean_age sd_age
<dbl> <dbl>
1 25.6 9.5
group_by()Oft wollen wir einen Datensatz jedoch nicht auf eine einzelne Zeile reduzieren bzw. den gesamten Datensatz zusammenfassen, sondern eine Zusammenfassung für jede (Unter-)Gruppe erstellen. Dies ist hilfreich, insbesondere da wir im Seminar mit Tagen verschachtelt in Personen arbeiten werden.
# illustrate use of group_by() and summarize()
df_beispiel |>
group_by(education) |>
summarize(ACT_c = mean(ACT_z, na.rm = TRUE))# A tibble: 6 × 2
education ACT_c
<int> <dbl>
1 0 -0.223
2 1 -0.219
3 2 -0.325
4 3 -0.0524
5 4 0.148
6 5 0.219
Ähnlich wie bei mutate() kann auch die Operation für summarize() komplexer sein, z. B. das Finden des Mittelwerts einer logischen Operation (d.h., logische Operatoren nutzend, wie &, <, >, | etc.) , um einen Anteil zu berechnen. Im Folgenden berechnen wir den Anteil von niedrigen SAT Verbal Scores (hier definiert als -1 Standardabweichungen oder niedriger) gruppiert nach Bildung.
df_beispiel |>
mutate(SATV_z = scale(SATV)) |>
group_by(education) |>
summarize(SATV_unterdurchschnitt = mean(SATV_z < -1, na.rm = TRUE))# A tibble: 6 × 2
education SATV_unterdurchschnitt
<int> <dbl>
1 0 0.193
2 1 0.2
3 2 0.25
4 3 0.135
5 4 0.0942
6 5 0.0851
Mit across() kannst du Zusammenfassungen (oder auch Änderungen mit mutate()) über mehrere Spalten hinweg auf dieselbe Weise durchführen. Wir werden hier nicht alle Möglichkeiten oder Details zu across() behandeln, aber es ist wichtig zu wissen, dass es möglich ist. Zum Beispiel:
df_beispiel |>
# ... calculate the mean of every numeric column in the dataset ...
summarise(across(where(is.numeric), mean, na.rm = TRUE)) |>
# ... and then round every column to one decimal place
mutate(across(everything(), round, digits = 2))Warning: There was 1 warning in `summarise()`.
ℹ In argument: `across(where(is.numeric), mean, na.rm = TRUE)`.
Caused by warning:
! The `...` argument of `across()` is deprecated as of dplyr 1.1.0.
Supply arguments directly to `.fns` through an anonymous function instead.
# Previously
across(a:b, mean, na.rm = TRUE)
# Now
across(a:b, \(x) mean(x, na.rm = TRUE))
# A tibble: 1 × 8
gender education age ACT SATV SATQ ACT_c ACT_z
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 1.65 3.16 25.6 28.6 612. 610. 0 0
summarize()Berechne min, max, mean, und SD aller Antworten auf dem selbstberichteten SAT Verbal Score (SATV).
# data_selfreport_tidy
df_beispiel |>
summarize()# A tibble: 1 × 0
Für die zweiten Übung arbeiten wir mit dem Datensatz BFI, der Big Five Items beinhaltet (s. Dokumentation des Datensatzes). Berechne, getrennt nach Gender, Mittelwert un SD von C1 (ein Gewissenhaftigkeit-Item).
data(bfi)
head(bfi) A1 A2 A3 A4 A5 C1 C2 C3 C4 C5 E1 E2 E3 E4 E5 N1 N2 N3 N4 N5 O1 O2 O3 O4
61617 2 4 3 4 4 2 3 3 4 4 3 3 3 4 4 3 4 2 2 3 3 6 3 4
61618 2 4 5 2 5 5 4 4 3 4 1 1 6 4 3 3 3 3 5 5 4 2 4 3
61620 5 4 5 4 4 4 5 4 2 5 2 4 4 4 5 4 5 4 2 3 4 2 5 5
61621 4 4 6 5 5 4 4 3 5 5 5 3 4 4 4 2 5 2 4 1 3 3 4 3
61622 2 3 3 4 5 4 4 5 3 2 2 2 5 4 5 2 3 4 4 3 3 3 4 3
61623 6 6 5 6 5 6 6 6 1 3 2 1 6 5 6 3 5 2 2 3 4 3 5 6
O5 gender education age
61617 3 1 NA 16
61618 3 2 NA 18
61620 2 2 NA 17
61621 5 2 NA 17
61622 3 1 NA 17
61623 1 2 3 21
summarize()
df_beispiel |>
summarize(min_satv = min(SATV, na.rm = TRUE),
max_satv = max(SATV, na.rm = TRUE),
mean_satv = mean(SATV, na.rm = TRUE),
sd_satv = sd(SATV, na.rm = TRUE))# A tibble: 1 × 4
min_satv max_satv mean_satv sd_satv
<int> <int> <dbl> <dbl>
1 200 800 612. 113.
bfi |> group_by(gender) |>
summarize(C1_na = sd(C1, na.rm = TRUE),
C1_mean = mean(C1, na.rm = TRUE))# A tibble: 2 × 3
gender C1_na C1_mean
<int> <dbl> <dbl>
1 1 1.24 4.48
2 2 1.24 4.52
Was ist der Unterschied zwischen mutate() und summarize()? Bekomme ich dasselbe Ergebnis, wenn ich die falsche Funktion verwende? Zum Beispiel: mutate(mean_age = mean(age, na.rm = TRUE)) vs.summarize(mean_age = mean(age, na.rm = TRUE)).
Am Ende von Data Wrangling Prozessen ist es sinnvoll, die Ergebnisse festzuhalten, damit wir sie nicht verlieren, bzw. beim nächsten Mal nicht alles von vorne ausführen müssen.
df_beispiel_standardisiert <- df_beispiel |> mutate(across(c("SATV", "SATQ", "ACT"),
scale))
save(df_beispiel_standardisiert, file = "../data/df_beispiel_standardisiert.RData")